package cn.sc.summer.mybatis.scan;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.sc.summer.mybatis.model.BaseTenantModel;
import com.baomidou.mybatisplus.annotation.TableName;
import com.google.common.base.CaseFormat;
import cn.sc.summer.constant.mybatisplus.MybatisConstant;
import cn.sc.summer.constant.util.AssertUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.annotations.Mapper;
import org.reflections.Reflections;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * 类名：扫描所有租户表信息
 *
 * @author a-xin
 * @date 2024/8/23 16:14
 */
@Slf4j
@Component
@ConditionalOnProperty(prefix = "tenant", name = "enable", havingValue = "true")
public class TenantScanner {

    /**
     * 所有继承了BaseTable的表信息，带有租户字段
     */
    public static final List<String> IGNORE_TENANT_TABLE = new ArrayList<>();

    @PostConstruct
    private void init() {
        getMapperInfo();
    }

    /**
     * 根据Mapper类继承泛型获取对应的数据库表映射实体类，再根据该实体类是否具有租户字段进行表抓取
     */
    private static void getMapperInfo() {
        Reflections reflection = new Reflections(MybatisConstant.MAPPER_PACKAGE);
        Set<Class<?>> tableNameClass = reflection.getTypesAnnotatedWith(Mapper.class);
        List<String> tenantTable = new ArrayList<>();
        tableNameClass.stream().forEach(mapperInfo -> {
            Type[] genericInterfaces = mapperInfo.getGenericInterfaces();
            if (ObjectUtil.isNotNull(genericInterfaces)) {
                Type genericInterface = genericInterfaces[0];
                ParameterizedType parameterizedType = (ParameterizedType) genericInterface;
                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                Type actualTypeArgument = actualTypeArguments[0];
                String typeName = actualTypeArgument.getTypeName();
                try {
                    Class<?> typeClass = Class.forName(typeName);
                    if (typeClass.getSuperclass().equals(BaseTenantModel.class)) {
                        TableName tableName = typeClass.getAnnotation(TableName.class);
                        tenantTable.add(tableName.value());
                    } else {
                        Field[] fields = typeClass.getDeclaredFields();
                        for (Field field : fields) {
                            if (field.getName().equals(MybatisConstant.TENANT_FILED)) {
                                String simpleName = typeClass.getSimpleName();
                                String tableNameUnder = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, simpleName);
                                tenantTable.add(tableNameUnder);
                            }
                        }
                    }
                } catch (ClassNotFoundException exception) {
                    exception.printStackTrace();
                }
            }
        });
        AssertUtil.functionIfPre(tenantTable, CollUtil::isNotEmpty, () -> {
            log.info("==> Scan the tenant table: {}", String.join(", ", tenantTable));
            IGNORE_TENANT_TABLE.addAll(tenantTable);
        });
    }

}
